<?php
declare(strict_types=1);

namespace app\middleware;

use app\traits\ResponseTrait;
use think\facade\{Cache, Request};
use Closure;

class IdempotencyMiddleware
{
    use ResponseTrait;

    /**
     * 需要幂等性检查的请求方法
     */
    protected $methods = ['POST', 'PUT', 'PATCH', 'DELETE'];

    /**
     * 处理请求
     */
    public function handle(Request $request, Closure $next)
    {
        // 只检查特定的请求方法
        if (!in_array($request->method(), $this->methods)) {
            return $next($request);
        }

        // 获取幂等性token
        $token = $request->header('Idempotency-Token');
        if (empty($token)) {
            return $this->error('缺少幂等性token', 400);
        }

        // 生成缓存key
        $cacheKey = $this->getCacheKey($request, $token);

        // 检查是否已处理过该请求
        $response = Cache::get($cacheKey);
        if ($response !== null) {
            return json($response); // 返回之前的处理结果
        }

        // 设置处理中标记，防止并发请求
        $processingKey = "processing:{$cacheKey}";
        if (Cache::get($processingKey)) {
            return $this->error('请求正在处理中，请勿重复提交', 409);
        }
        Cache::set($processingKey, 1, 30); // 设置30秒的处理超时时间

        try {
            // 处理请求
            $response = $next($request);
            
            // 缓存响应结果
            if ($response->getCode() == 200) {
                Cache::set($cacheKey, $response->getData(), 86400); // 缓存24小时
            }
            
            return $response;
            
        } finally {
            // 清除处理中标记
            Cache::delete($processingKey);
        }
    }

    /**
     * 生成缓存key
     */
    protected function getCacheKey(Request $request, string $token): string
    {
        $identifier = '';
        
        // 获取用户标识
        if ($user = $request->user) {
            $identifier = "user:{$user->id}";
        } elseif ($merchant = $request->merchant) {
            $identifier = "merchant:{$merchant->id}";
        } elseif ($admin = $request->admin) {
            $identifier = "admin:{$admin->id}";
        }

        // 组合请求信息
        $path = $request->pathinfo();
        $method = $request->method();
        
        return "idempotency:{$identifier}:{$method}:{$path}:{$token}";
    }
} 